/**
 * Copyright Imre Tabur.
 * Ask licence for use.
 **/
// Controls related variables
var htmlElementIDList = ["someValue"]; // on HTML set with same id value and list them here and add new var with same name.
var someValue = null;

// GL related variables
var canvas = null;
var type = null;
var is3D = false;
var context = null;
var shaders = new Array();
var vertexPosAttribLocation = null;
var uModelViewMatrix = null;
var uPerspectiveMatrix = null;
var colorMatrix = null;
var ajaxData = null;

var useAjax = false;

function doTask(obj) {
    if (useAjax) {
        ajaxCall("js/data.json");
		drawFrame();
    } else {
        drawFrame();
    }
}

function doOnSlider(obj, txt) {
    // Just for example
    var txt = obj.value;
    var number = parseInt(txt);
}

function initPage(name) {
    log("Start initPage: " + name);
    initControls(htmlElementIDList);
    canvas = findElement(name);
    init3D();
    initShaders();
}

function initControls(namesList) {
    for (i = 0; i < namesList.length; ++i) {
        var elem = findElement(namesList[i]);
        var txt = elem.value;
        this[namesList[i]] = txt;
    }
}

function log(msg) {
    console.log(msg);
}

function findElement(elementId) {
    return document.getElementById(elementId);
}

function init3D() {
    var names = ["experimental-webgl", "webgl", "webkit-3d", "moz-webgl"];
    var i = 0;
    for (i = 0; i < names.length; ++i) {
        try {
            var tp = names[i];
            context = canvas.getContext(tp);
            if (context !== null) {
                //context.viewport(0, 0, getX(), getY());
                type = tp;
                is3D = true;
                break;
            }
        } catch (e) {
            log(e.toString());
            throw e;
        }
    }
}

function initShaders() {
    if (context === undefined || context === null) {
        throw "There's no WebGL context available.";
    }
    addShader(createShader("shader-vs"));
    addShader(createShader("shader-fs"));
    context.program = context.createProgram();
    attachShaders();
    context.linkProgram(context.program);
    if (!context.getProgramParameter(context.program, context.LINK_STATUS)) {
        deleteAll();
        throw "Unable to initialise shaders";
    }
    context.useProgram(context.program);
    var vertexPosition = getShaderAttribute("vertexPosition");
    context.enableVertexAttribArray(vertexPosition);
    context.clearColor(0.5, 0.5, 0.5, 0.7);
    context.clearDepth(1.0);
    context.enable(context.DEPTH_TEST);
    context.depthFunc(context.LEQUAL);
    vertexPosAttribLocation = getShaderAttribute("vertexPosition");
    uModelViewMatrix = getShaderUniform("modelViewMatrix");
    uPerspectiveMatrix = getShaderUniform("perspectiveMatrix");
    colorMatrix = getShaderAttribute("aVertexColor");
    context.enableVertexAttribArray(colorMatrix);
}

function createShader(id) {
    var element = findElement(id);
    var shaderType = getShaderTypeFromElement(element);
    var scriptTxt = extractShaderScript(element);
    var shader = context.createShader(shaderType);
    context.shaderSource(shader, scriptTxt);
    context.compileShader(shader);
    if (!context.getShaderParameter(shader, context.COMPILE_STATUS)) {
        context.deleteShader(shader);
        throw (context.getShaderInfoLog(shader)).toString();
    }
    return shader;
}

function getShaderTypeFromElement(element) {
    var ret = null;
    if ("x-shader/x-vertex" === element.type) {
        ret = context.VERTEX_SHADER;
    } else if ("x-shader/x-fragment" === element.type) {
        ret = context.FRAGMENT_SHADER;
    } else {
        throw "Can't find corresponding shader for type: " + type;
    }
    return ret;
}

function extractShaderScript(element) {
    var scriptTxt = "";
    var node = element.firstChild;
    while (node) {
        if (node.nodeType === 3) {
            scriptTxt += node.textContent;
        }
        node = node.nextSibling;
    }
    return scriptTxt;
}

function getShaderUniform(id) {
    var ret = context.getUniformLocation(context.program, id);
    return ret;
}

function getShaderAttribute(id) {
    var ret = context.getAttribLocation(context.program, id);
    return ret;
}

function addShader(shader) {
    shaders[shaders.length] = shader;
    return shader;
}

function attachShaders() {
    var i = 0;
    for (i = 0; i < shaders.length; i++) {
        context.attachShader(context.program, shaders[i]);
    }
}

function deleteAll() {
    context.deleteProgram(context.program);
    deleteShaders();
}

function deleteShaders() {
    var i = 0;
    for (i = 0; i < shaders.length; i++) {
        context.deleteProgram(shaders[i]);
    }
}

function getX() {
    return canvas.width;
}

function getY() {
    return canvas.height;
}

function drawFrame() {
    var fieldOfView = 30.0;
    var aspectRatio = canvas.width / canvas.height;
    var nearPlane = 1.0;
    var farPlane = 10000.0;
    var top = nearPlane * Math.tan(fieldOfView * Math.PI / 360.0);
    var bottom = -top;
    var right = top * aspectRatio;
    var left = -right;
    var a = (right + left) / (right - left);
    var b = (top + bottom) / (top - bottom);
    var c = (farPlane + nearPlane) / (farPlane - nearPlane);
    var d = (2 * farPlane * nearPlane) / (farPlane - nearPlane);
    var x = (2 * nearPlane) / (right - left);
    var y = (2 * nearPlane) / (top - bottom);
    /*
     * 2 (two) x 3 (triangles) x 3 (x,y,z).
     * x,y,z,
     **/
    var perspectiveMatrix = [
        x, 0, a, 0,
        0, y, b, 0,
        0, 0, c, d,
        0, 0, -1, 0
    ];
    var modelViewMatrix = [
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    ];

    var colors = [
        1.0, 0.0, 0.0, 1.0, // red + alpha (!?) 
        0.0, 1.0, 0.0, 1.0, // green  
        0.0, 0.0, 1.0, 1.0     // blue  
    ];
    var squareVerticesColorBuffer = context.createBuffer();
    context.bindBuffer(context.ARRAY_BUFFER, squareVerticesColorBuffer);
    context.bufferData(context.ARRAY_BUFFER, new Float32Array(colors), context.STATIC_DRAW);
    context.vertexAttribPointer(colorMatrix, 4, context.FLOAT, false, 0, 0);

    var vertices = new Float32Array([
        0.0, 1.0, 4.0,
        -1.0, -1.0, 4.0,
        1.0, -1.0, 4.0,
        0.0, -1.0, 4.0,
        -1.0, 1.0, 4.0,
        1.0, 1.0, 4.0
    ]);
    buildBuffer(vertices, 3, 6);
    context.vertexAttribPointer(vertexPosAttribLocation, 3.0, context.FLOAT, false, 0, 0);
    context.uniformMatrix4fv(uModelViewMatrix, false, new Float32Array(perspectiveMatrix));
    context.uniformMatrix4fv(uPerspectiveMatrix, false, new Float32Array(modelViewMatrix));
    context.drawArrays(context.TRIANGLES, 0, vertices.length / 6.0);
    context.drawArrays(context.TRIANGLES, 3, vertices.length / 6.0);

    var paper = new Float32Array([
        -2.0, -1.0, 4.0,
        -2.0, -1.0, 8.0,
        1.0, -1.0, 8.0
    ]);
    buildBuffer(paper, 3, 3);
    context.vertexAttribPointer(vertexPosAttribLocation, 3.0, context.FLOAT, false, 0, 0);
    context.uniformMatrix4fv(uModelViewMatrix, false, new Float32Array(perspectiveMatrix));
    context.uniformMatrix4fv(uPerspectiveMatrix, false, new Float32Array(modelViewMatrix));
    context.drawArrays(context.TRIANGLES, 0, paper.length / 3.0);
    /*
     var fan =  new Float32Array([
     -1.0, -0.5, -4.0,
     2.0, -0.5, -4.0,
     0.0,  0.5, -4.0,
     -1.5,  0.0, -4.0,
     -1.8, -1.0, -4.0,
     0.2, -1.5, -4.0,
     1.0, -0.5, -4.0,
     ]);
     this.buildBuffer(fan, 3, 7);
     this.context.vertexAttribPointer(this.vertexPosAttribLocation, 3.0, this.context.FLOAT, false, 0, 0);
     this.context.uniformMatrix4fv(this.uModelViewMatrix, false, new Float32Array(perspectiveMatrix));
     this.context.uniformMatrix4fv(this.uPerspectiveMatrix, false, new Float32Array(modelViewMatrix));
     this.context.drawArrays(this.context.TRIANGLES, 0, 3);
     */
}

function buildBuffer(array, itemSize, itemsNum) {
    var buffer = this.context.createBuffer();
    this.context.bindBuffer(this.context.ARRAY_BUFFER, buffer);
    this.context.bufferData(this.context.ARRAY_BUFFER, new Float32Array(array), this.context.STATIC_DRAW);
    buffer.itemSize = itemSize;
    buffer.numItems = itemsNum;
    return buffer;
}
